/*
Metronom for Android
    Copyright (C) 2012  Zdeněk Janeček <jan.zdenek@gmail.com>

	Contributors: Jiří Janeček

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package kiv.janecekz;

import kiv.janecekz.R.id;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.text.method.LinkMovementMethod;
import android.view.Display;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.NumberPicker;
import android.widget.NumberPicker.OnValueChangeListener;
import android.widget.TextView;

public class Main extends Activity implements OnSharedPreferenceChangeListener,
        OnValueChangeListener {
    private int width;

    private Operator op;
    private Peeper peeper;
    private Tempomat tempo;
    private NumberPicker beatPicker;
    private NumberPicker bpmPicker;
    private PowerManager.WakeLock wl;
    private AlertDialog helpDialog;
    private ObserversUpdater ou;
    private View main;
    
    private VelocityTracker vt;
    private ImageView circle;
    private AnimatorSet inAnim;
    private AnimatorSet outAnim;

    // for debugging
    public static final String TAG = "Metronom";

    private static final int HELLO_ID = 1;
    private Notification notification;
    private NotificationManager mNotificationManager;

    private final int DOUBLE_CLICK_DELAY = 500;
    private float biggestSpeed;
    private long startTime;
    private long stopTime;
    private boolean stopping = false;

    /**
     * Main initialization of all Views and Events.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
        if (SharedPref.getOrient(getApplicationContext())) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        }
        setContentView(R.layout.main);

        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        main = findViewById(R.id.main);

        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        width = size.x;

        ImageView sun = (ImageView) findViewById(R.id.sun);
        peeper = new Peeper(getApplicationContext(), sun);

        circle = (ImageView) findViewById(id.circle);
        
        ObjectAnimator circleInAnim1 = ObjectAnimator.ofFloat(this.circle, "alpha", 0f, 1f);
        ObjectAnimator circleInAnim2 = ObjectAnimator.ofFloat(this.circle, "scaleY", 0f, 1f);
        circleInAnim1.setDuration(400);
        circleInAnim2.setDuration(400);
        
        ObjectAnimator circleOutAnim1 = ObjectAnimator.ofFloat(this.circle, "alpha", 1f, 0f);
        ObjectAnimator circleOutAnim2 = ObjectAnimator.ofFloat(this.circle, "scaleY", 1f, 0f);
        circleOutAnim2.setDuration(700);
        circleOutAnim2.setDuration(700);
        
        inAnim = new AnimatorSet();
        outAnim = new AnimatorSet();
        
        inAnim.play(circleInAnim1).with(circleInAnim2);
        outAnim.play(circleOutAnim1).with(circleOutAnim2);

        if (vt == null) {
            vt = VelocityTracker.obtain();
        }

        // initialize changeable things
        changeBg();
        changeSound();

        PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Beep");

        // Notification
        String ns = Context.NOTIFICATION_SERVICE;
        mNotificationManager = (NotificationManager) getSystemService(ns);

        beatPicker = (NumberPicker) findViewById(R.id.beatCount);
        bpmPicker = (NumberPicker) findViewById(R.id.bpmCount);

        bpmPicker.setMaxValue(Tempomat.MAX_BPM);
        bpmPicker.setMinValue(Tempomat.MIN_BPM);
        beatPicker.setValue(SharedPref.getBPM(getApplicationContext()));
        beatPicker.setMaxValue(4);
        beatPicker.setMinValue(1);

        beatPicker.setValue(SharedPref.getTime(getApplicationContext()));

        // Tempo picker
        beatPicker.setOnValueChangedListener(this);

        // Výběr
        bpmPicker.setOnValueChangedListener(this);

        SharedPreferences sp = getSharedPreferences(SharedPref.PREFS_NAME,
                MODE_PRIVATE);
        sp.registerOnSharedPreferenceChangeListener(this);

        ou = new ObserversUpdater();
        ou.addView(bpmPicker);

        tempo = new Tempomat(getApplicationContext());
        tempo.setBPM(SharedPref.getBPM(getApplicationContext()));

        tempo.addObserver(ou);

        // Nápověda
        AlertDialog.Builder help = new AlertDialog.Builder(Main.this);
        help.setTitle(R.string.help);
        help.setMessage(R.string.helpText);
        help.setIcon(android.R.drawable.ic_menu_info_details);
        helpDialog = help.create();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        menu.findItem(R.id.set)
                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);

        Intent prefsIntent = new Intent(getApplicationContext(), Setup.class);

        menu.findItem(R.id.set).setIntent(prefsIntent);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.help:
            helpDialog.show();
            ((TextView) helpDialog.findViewById(android.R.id.message))
                    .setMovementMethod(LinkMovementMethod.getInstance());
            return true;

        case R.id.set:
            startActivity(item.getIntent());
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        SharedPref.setBPM(getApplicationContext(), tempo.getBPM());

        if (!wl.isHeld() || this.isFinishing()) {
            op.interrupt();
            tempo.deleteObserver(op);
        }

        if (SharedPref.getNotify(getApplicationContext())
                && !this.isFinishing() && !this.isChangingConfigurations()) {
            Context context = getApplicationContext();
            int icon = R.drawable.ic_launcher;
            CharSequence tickerText = getString(R.string.nTicker);
            long when = System.currentTimeMillis();

            Notification.Builder nb = new Notification.Builder(context);
            // notification = new Notification(icon, tickerText, when);
            // //deprecated

            Intent notificationIntent = new Intent(context, Main.class);
            notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                    | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                    notificationIntent, HELLO_ID);

            CharSequence contentTitle = getString(R.string.app_name);
            CharSequence contentText = getString(R.string.nText);

            nb.setContentTitle(contentTitle);
            nb.setContentText(contentText);
            nb.setContentInfo(tickerText);
            nb.setWhen(when);
            nb.setSmallIcon(icon);
            nb.setContentIntent(contentIntent);

            notification = nb.getNotification();

            mNotificationManager.notify(HELLO_ID, notification);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        mNotificationManager.cancel(HELLO_ID);
        if (!wl.isHeld()) {
            op = new Operator(peeper, wl);
            tempo.addObserver(op);

            tempo.refreshObservers();

            op.start();
            if (SharedPref.getPlay(getApplicationContext())) {
                op.setPlay(true);
            }
        }
    }

    @Override
    protected void onDestroy() {
        op.interrupt();
        peeper.cleanup();
        vt.clear();
        super.onDestroy();
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
            String key) {

        if (key.equals(getString(R.string.pref_key_bg))) {
            changeBg();
        } else if (key.equals(getString(R.string.pref_key_sound))) {
            changeSound();
        } else if (key.equals(getString(R.string.pref_key_orientation))) {
            if (SharedPref.getOrient(getApplicationContext())) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
            }
        }
    }

    private void changeBg() {
        switch (SharedPref.getTheme(getApplicationContext())) {
        case 0:
            main.setBackgroundResource(R.drawable.bg_morning);
            break;
        case 1:
            main.setBackgroundResource(R.drawable.bg_sunset);
            break;
        case 2:
            main.setBackgroundResource(R.drawable.bg_night);
            break;

        default:
            break;
        }
    }

    private void changeSound() {
        int value = SharedPref.getSound(getApplicationContext());
        switch (value) {
        case 0:
        case 1:
        case 2:
            peeper.setSound((byte) value);
            break;
        default:
            break;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
            if (stopping
                    && ((event.getEventTime() - stopTime) < DOUBLE_CLICK_DELAY)) {
                Context con = getApplicationContext();
                SharedPref.setPlay(con, !SharedPref.getPlay(con));
                op.setPlay(SharedPref.getPlay(con));

                stopping = false;
            } else {
                inAnim.start();
                biggestSpeed = 0;
                startTime = event.getEventTime();
            }
        } else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
            circle.setX(event.getX() - circle.getWidth() / 2);
            circle.setY(event.getY() - circle.getHeight() / 2 - ((View) circle.getParent()).getTop()-100);
            
            vt.addMovement(event);
            vt.computeCurrentVelocity(1000);

            float speed = vt.getXVelocity();
            if (biggestSpeed < speed)
                biggestSpeed = speed;

            if (event.getEventTime() - startTime > DOUBLE_CLICK_DELAY) {
                float velocity = Math.abs(vt.getXVelocity()) > 20 ? vt.getXVelocity() : 0;
                tempo.setBPM((int) (tempo.getBPM() + (velocity / width) * 5));
            }

        } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
            if ((Math.abs(biggestSpeed) < 150)
                    && ((event.getEventTime() - startTime) < DOUBLE_CLICK_DELAY)) {
                stopping = true;
                stopTime = event.getEventTime();
            }
            outAnim.start();
        }

        return true;
    }

    public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
        if (picker.equals(beatPicker)) {
            peeper.setTime(newVal);
        }
        if (picker.equals(bpmPicker)) {
            tempo.setBPM(newVal);
        }
    }
}